package sistemaDeLaCarrera;

import java.util.ArrayList;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import java.util.SortedSet;

import Catedras.Catedra;




import excepciones.AlumnoNoAproboTodasLasCorrelatividadesException;
import excepciones.AlumnoNoInscriptoEnCarreraException;
import excepciones.AlumnoNoRegularException;
import excepciones.AlumnoYaEsRegularException;
import excepciones.AlumnoYaInscriptoException;
import excepciones.CatedraNoPerteneceAMateriaException;
import excepciones.CupoLLenoException;
import excepciones.HorariosSuperpuestosException;
import excepciones.MateriaNoFormaParteDelPlanDeEstudioException;



import alumno.*;



public class Carrera implements Comparable<Carrera>{
	private String nombre;
	private Set<Alumno> alumnos = new HashSet<Alumno>();
	private SortedSet<AlumnoEnCarrera> alumnosEnCarrera = new TreeSet<AlumnoEnCarrera>();
	private List<PlanDeEstudio> planesDeEstudios = new ArrayList<PlanDeEstudio>();
	//El contador de legajos se utiliza al momento de inscribir un alumno a la carrera
	private int contadorDeLegajos = 1;
	private PlanDeEstudio planDeEstudioActual;	
	
	public Carrera(String nombre,PlanDeEstudio planDeEstudioActual) {
		this.nombre = nombre;		
		this.planDeEstudioActual = planDeEstudioActual;
	}

	//COMPORTAMIENTO		
	/**
	 * Inscribe el alumno a la carrera y le asigna un legajo. Si ya estaba inscripto arroja una excepcion.
	 * @param alumno
	 * @throws AlumnoYaInscriptoException 
	 */
	public synchronized void inscribirAlumno(Alumno alumno) throws AlumnoYaInscriptoException{
		if(this.alumnos.contains(alumno))
			throw new AlumnoYaInscriptoException();
		else{			
			AlumnoEnCarrera alumnoEnCarrera = new AlumnoEnCarrera(alumno,this,contadorDeLegajos);			
			this.contadorDeLegajos++;
			this.alumnos.add(alumno);
			this.alumnosEnCarrera.add(alumnoEnCarrera);
			alumnoEnCarrera.setPlanDeEstudio(this.planDeEstudioActual);
			//Este metodo tiene especial importancia para contemplar el caso en que un 
			//alumno tenga aprobado el curso de ingreso ya que las materias de dicho curso
			//son comunes a todas las carreras de la universidad
			alumnoEnCarrera.agregarEquivalenciasDeOtrasCarrerasDeLaUNQ();
			}			
	}	

	/**
	 * Comprueba si un alumno es regular
	 * @param legajo
	 * @throws AlumnoNoRegularException 
	 */
	public void comprobarRegularidad(AlumnoEnCarrera alumnoEnCarrera) throws AlumnoNoRegularException{		
		if(!alumnoEnCarrera.esRegular())
			throw new AlumnoNoRegularException();	
	}
	
	/**
	 * Comprueba que un alumno esta inscripto en la carrera
	 * @param alumnoEnCarrera
	 * @throws AlumnoNoInscriptoEnCarreraException 
	 */
	public void comprobarQueAlumnoPerteneceACarrera(AlumnoEnCarrera alumnoEnCarrera) throws AlumnoNoInscriptoEnCarreraException{
		if(!alumnosEnCarrera.contains(alumnoEnCarrera))
			throw new AlumnoNoInscriptoEnCarreraException();
	}
	
	/**
	 * Agrega un alumno a la catedra de la materia que se pasan por parametro.
	 * Si el cupo de la catedra esta lleno se dispara una excepcion 
	 * @param alumnoEnCarrera
	 * @param materia
	 * @param catedra
	 * @throws AlumnoNoInscriptoEnCarreraException	 
	 * @throws CatedraNoPerteneceAMateriaException
	 * @throws AlumnoNoRegularException
	 * @throws AlumnoNoAproboTodasLasCorrelatividadesException 
	 * @throws MateriaNoFormaParteDelPlanDeEstudioException 
	 * @throws HorariosSuperpuestosException 
	 * @throws CupoLLenoException 
	 * @throws AlumnoYaInscriptoException 
	 */
	public void inscribirAlumnoEnCatedra(AlumnoEnCarrera alumnoEnCarrera,Materia materia,Catedra catedra) throws AlumnoNoInscriptoEnCarreraException, CatedraNoPerteneceAMateriaException, AlumnoNoRegularException, AlumnoNoAproboTodasLasCorrelatividadesException, MateriaNoFormaParteDelPlanDeEstudioException, CupoLLenoException, HorariosSuperpuestosException, AlumnoYaInscriptoException{		
		this.comprobarQueAlumnoPerteneceACarrera(alumnoEnCarrera);
		this.comprobarRegularidad(alumnoEnCarrera);
		materia.inscribirAlumno(alumnoEnCarrera,catedra);									
	}
	/**
	 * Quita un alumno de la catedra de la materia que se pasan por parametro.	
	 * Si el alumno no esta inscripto en la catedra se dispara una excepcion 
	 * @param legajo
	 * @param nombreMateria
	 * @param nombreCatedra 
	 * @throws MateriaNoPerteneceACarreraException 
	 * @throws AlumnoNoInscriptoEnCarreraException 
	 * @throws CatedraNoPerteneceAMateriaException 
	 */
	public void desinscribirAlumnoDeCatedra(AlumnoEnCarrera alumnoEnCarrera,Materia materia,Catedra catedra) throws AlumnoNoInscriptoEnCarreraException, CatedraNoPerteneceAMateriaException, AlumnoNoRegularException{		
		comprobarQueAlumnoPerteneceACarrera(alumnoEnCarrera);
		materia.desinscribirAlumno(alumnoEnCarrera,catedra);									
	}
	
	/**
	 * Reincorpora el alumnoEnCarrera. Este recupera la regularidad. 
	 * @param alumnoEnCarrera
	 * @throws AlumnoYaEsRegularException
	 */
	public void reincorporarAlumnoEnCarrera(AlumnoEnCarrera alumnoEnCarrera) throws AlumnoYaEsRegularException{
		alumnoEnCarrera.recuperarRegularidad();
	}
	

	/**
	 * Retorna un map con los alumnosEnCarrera y sus coeficientes
	 * @return
	 */
	public Map<AlumnoEnCarrera,Double> publicarCoeficientes(){
		Map<AlumnoEnCarrera,Double> coeficientes = new Hashtable<AlumnoEnCarrera,Double>();
		for(AlumnoEnCarrera alumnoEnCarrera : this.alumnosEnCarrera){
			coeficientes.put(alumnoEnCarrera, alumnoEnCarrera.coeficiente());
		}
		return coeficientes;
	}	

	
	public AlumnoEnCarrera buscarAlumno(int legajo){
		SortedSet<AlumnoEnCarrera> alumnos = this.getAlumnosEnCarrera();
		AlumnoEnCarrera alumno = alumnos.first();
		while(!alumnos.isEmpty()){
			if(alumno.getLegajo() == legajo)
				break;
			alumnos = alumnos.tailSet(null); // revisar
		}
		return alumno;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Carrera)
			return this.nombre == ((Carrera) obj).getNombre();
		else
			return false;
	}
	
	@Override
	public int compareTo(Carrera carrera) {
		return this.nombre.compareTo(carrera.getNombre());
	}

	//GETTERS AND SETTERS 
	
	public String getNombre() {
		return nombre;
	}


	public Set<Alumno> getAlumnos() {
		return alumnos;
	}

	public void setAlumnos(Set<Alumno> alumnos) {
		this.alumnos = alumnos;
	}

	public SortedSet<AlumnoEnCarrera> getAlumnosEnCarrera() {
		return alumnosEnCarrera;
	}

	public void setAlumnosEnCarrera(SortedSet<AlumnoEnCarrera> alumnosEnCarrera) {
		this.alumnosEnCarrera = alumnosEnCarrera;
	}

	public List<PlanDeEstudio> getPlanesDeEstudios() {
		return planesDeEstudios;
	}

	public void setPlanesDeEstudios(ArrayList<PlanDeEstudio> planesDeEstudios) {
		this.planesDeEstudios = planesDeEstudios;
	}

	public int getContadorDeLegajos() {
		return contadorDeLegajos;
	}

	public void setContadorDeLegajos(int contadorDeLegajos) {
		this.contadorDeLegajos = contadorDeLegajos;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	
	public PlanDeEstudio getPlanDeEstudioActual() {
		return planDeEstudioActual;
	}	

	
}
